﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using UFrame;
using UnityEngine;

namespace Database
{
    /// <summary>
    /// 资源信息管理。
    /// </summary>
    public static class CfgMa
    {
        /// <summary>
        /// Orm类的字段信息（内部分析用）。
        /// </summary>
        private class ColumnInfo
        {
            /// <summary>
            /// Orm类的字段名（已转大写）。
            /// </summary>
            public string mColumnName;

            public FieldInfo mFieldInfo;

            /// <summary>
            /// Orm类数组字段的维度。
            /// </summary>
            public int mArrayIndex;
        }


        public enum ExcelReadMode
        {
            /// <summary>
            /// 字段名和变量名在同一行
            /// </summary>
            OneRow = 0,

            /// <summary>
            /// 字段名和变量名分别站一行
            /// </summary>
            TwoRow = 1,
        }

        private static IDictionary<Type, Func<string, object>> mTypeDic; //帮助值转换， 比如0 = false 1 = true

        private static int[] ignoreRow;

        public static bool isReadExcel;

        public static ExcelReadMode mode = ExcelReadMode.TwoRow; //0 代表两行设计  1代表一行设计

        public static string xlsx = ".xlsx";

        public static string excelPath = @"..\..\..\CSV\{0}.xlsx";

        public static IExcelTool excelTool;

        public static int fieldRow = 0; //字段

        /// <summary>
        /// 读取二进制文件
        /// </summary>
        public static bool isReadByte;

        /// <summary>
        /// 根据反射自动读取
        /// </summary>
        public static bool isReadByteRef = false;

        /// <summary>
        /// 静态构造函数（初始化全局缓存信息）。
        /// </summary>
        static CfgMa()
        {
            RegisterConvert();
            ignoreRow = new[] { -1, 0, };
        }


        public static void LoadResource<T>(string name, IDictionary<int, T> configDic) where T : BaseOrm, new()
        {
            if (isReadExcel)
            {
                string path = Application.dataPath + string.Format(excelPath, name);
                excelTool.ParseExcel(path, configDic);
            }
            else if (isReadByte) //读取二进制文件
            {
                TextAsset txtAsset = ResourcesMa.Load<TextAsset>("Assets/bawAsset/CSV/" + name + ".bytes");
                if (!txtAsset)
                {
                    Debug.Log("Not Find TextAsset Resource: " + name);
                }

                ParseBinary<T>(txtAsset.name, txtAsset.bytes, configDic);
            }
            else
            {
                TextAsset txtAsset = ResourcesMa.Load<TextAsset>("Assets/bawAsset/CSV/" + name + ".csv");
                if (!txtAsset)
                {
                    Debug.Log("Not Find TextAsset Resource: " + name);
                }
                else
                {
                    if (mode == ExcelReadMode.TwoRow)
                    {
                        ParseTwo(txtAsset.name, txtAsset.text, configDic);
                    }

                    if (mode == ExcelReadMode.OneRow)
                    {
                        ParseOne(txtAsset.name, txtAsset.text, configDic);
                    }
                }
            }
        }

        public static void ParseBinary<T>(string fileName, byte[] dataAry, IDictionary<int, T> configDic)
            where T : BaseOrm, new()
        {
            MemoryStream m = new MemoryStream(dataAry);
            BinaryReader r = new BinaryReader(m);

            configDic.Clear();

            while (r.BaseStream.Position != r.BaseStream.Length)
            {
                T t = new T();
                if (isReadByteRef)
                {
                    var type = t.GetType();
                    var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

                    foreach (var item in fields)
                    {
                        string fieldName = item.FieldType.Name;
                        object o = null;
                        if (fieldName == "Int32")
                        {
                            o = r.ReadInt32();
                        }
                        else if (fieldName == "Int16")
                        {
                            o = r.ReadInt16();
                        }
                        else if (fieldName == "Int64" || fieldName == "long")
                        {
                            o = r.ReadInt64();
                        }
                        else if (fieldName == "Boolean")
                        {
                            o = r.ReadBoolean();
                        }
                        else if (fieldName == "String" || fieldName == "string")
                        {
                            o = r.ReadString();
                        }
                        else if (fieldName == "Decimal" || fieldName == "Single")
                        {
                            o = r.ReadDecimal();
                        }
                        else if (fieldName == "Double")
                        {
                            o = r.ReadDouble();
                        }
                        else if (fieldName == "float")
                        {
                            o = r.ReadSingle();
                        }

                        item.SetValue(t, o);
                    }
                }
                else
                {
                    t.LoadBinary(r);
                    t.uniqueId = (int)t.GetType().GetField("id").GetValue(t);
                }

                if (configDic.ContainsKey(t.uniqueId))
                {
                    Debug.LogError(fileName + "重复包含Id: " + t.uniqueId);
                }
                else
                {
                    configDic.Add(t.uniqueId, t);
                }
            }
        }


        public static void ParseOne<T>(string fileName, string content, IDictionary<int, T> configDic)
            where T : BaseOrm, new()
        {
            //文件格式检查
            //Debug.Log( "正在解析资源：" + aAssetName );
            List<string> txtLines =
                content.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries).ToList();

            //导入数据的列检查和筛选
            Dictionary<int, ColumnInfo> columnInfodDic = new Dictionary<int, ColumnInfo>();
            Dictionary<string, FieldInfo> fieldDic = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public)
                .ToDictionary(c => c.Name.ToUpper(), c => c);

            int iCounter = 0;
            Dictionary<int, string> columnDic = txtLines[fieldRow].Split(new[] { "," }, StringSplitOptions.None)
                .ToDictionary(c => iCounter++, c => c.ToUpper());

            foreach (KeyValuePair<string, FieldInfo> itr in fieldDic)
            {
                if (columnDic.Values.Contains(itr.Key))
                {
                    bool isArray = itr.Value.FieldType.IsArray;
                    iCounter = 0;
                    for (int i = 0; i < columnDic.Count; i++)
                    {
                        if (columnDic[i] == itr.Key)
                        {
                            columnInfodDic.Add(i,
                                new ColumnInfo
                                {
                                    mColumnName = itr.Key,
                                    mFieldInfo = itr.Value,
                                    mArrayIndex = isArray ? iCounter : 0
                                });
                            if (isArray)
                            {
                                iCounter++;
                            }
                            else
                            {
                                break;
                            }
                        }
                    }
                }
                else
                {
                    Debug.Log(string.Format("资源文件 <{0}> 不存在ORM类 <{1}> 所需的 <{2}> 字段", fileName, typeof(T).Name,
                        itr.Key));
                    return;
                }
            }

            //动态加载数据到缓冲集合中
            configDic.Clear();

            for (int rowIndex = 0; rowIndex < txtLines.Count; rowIndex++)
            {
                if (ignoreRow.Contains(rowIndex))
                {
                    continue;
                }

                int sID = -1;
                T ormObj = Activator.CreateInstance<T>();

                string[] txtLine = txtLines[rowIndex].Split(new[] { "," }, StringSplitOptions.None);

                for (int col = 0; col < txtLine.Length; col++)
                {
                    if (!columnInfodDic.ContainsKey(col) || columnInfodDic[col].mArrayIndex > 0)
                    {
                        continue;
                    }

                    FieldInfo fInfo = columnInfodDic[col].mFieldInfo;
                    Type fType = fInfo.FieldType;

                    //不是数组，直接赋值
                    if (!fType.IsArray)
                    {
                        object value = mTypeDic.ContainsKey(fType) ? mTypeDic[fType](txtLine[col]) : txtLine[col];
                        fInfo.SetValue(ormObj, value);
                        //处理主键
                        if (col == 0)
                        {
                            int temp = 0;
                            if (int.TryParse(txtLine[col], out temp))
                            {
                                sID = temp;
                            }
                            else
                            {
                                if (rowIndex >= 3)
                                {
                                    ormObj = default(T);
                                    break;
                                }

                                Debug.LogFormat("{0}表的主键配置有错，应该为int型 行{1} 内容{2}", fileName, col, txtLine[col]);
                            }
                        }
                    }
                    else
                    {
                        //是数组，需查找所有的相同字段名
                        IList<int> arrayIndexs = (
                            from itr in columnInfodDic
                            where itr.Value.mColumnName == columnInfodDic[col].mColumnName
                            select itr.Key
                        ).ToList();

                        fType = fType.GetElementType();
                        Array objAry = Array.CreateInstance(fType, arrayIndexs.Count);
                        for (int k = 0; k < arrayIndexs.Count; k++)
                        {
                            objAry.SetValue(
                                mTypeDic.ContainsKey(fType)
                                    ? mTypeDic[fType](txtLine[arrayIndexs[k]])
                                    : txtLine[arrayIndexs[k]], k);
                        }

                        fInfo.SetValue(ormObj, objAry);
                    }
                }

                if (ormObj == null)
                {
                    continue;
                }

                if (configDic.ContainsKey(sID))
                {
                    configDic[sID] = ormObj;
                    Debug.LogWarning(string.Format("资源文件 <{0}> 的Key <{1}> 重复，系统自动进行覆盖", fileName, sID));
                }
                else
                {
                    configDic.Add(sID, ormObj);
                }
            }
        }

        /// <summary>
        /// 进行CSV资源文件解析，并加载到缓冲集合中。
        /// </summary>
        /// <typeparam name="T">Orm类。</typeparam>
        /// <param name="aAssetName">资源文件名称。</param>
        /// <param name="aTxtContent">资源文件内容。</param>
        /// <param name="aConfigDic">要装载的缓冲集合。</param>
        public static void ParseTwo<T>(string aAssetName, string aTxtContent, IDictionary<int, T> aConfigDic)
            where T : BaseOrm, new()
        {
            //文件格式检查
            //Debug.Log( "正在解析资源：" + aAssetName );
            List<string> txtLines =
                aTxtContent.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries).ToList();

            //导入数据的列检查和筛选
            Dictionary<int, ColumnInfo> columnInfodDic = new Dictionary<int, ColumnInfo>();
            Dictionary<string, FieldInfo> fieldDic = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public)
                .ToDictionary(c => c.Name, c => c);

            string[] ary = txtLines[fieldRow].Split(new[] { "," }, StringSplitOptions.None);

            for (int i = 0; i < ary.Length; i++)
            {
                string[] t = ary[i].Split(new[] { ":" }, StringSplitOptions.None);
                string fieldName = t[0];
                string fieldType = t[1];
                columnInfodDic.Add(i, new ColumnInfo()
                {
                    mColumnName = fieldName,
                    mFieldInfo = fieldDic[fieldName],
                });
            }

            //动态加载数据到缓冲集合中
            aConfigDic.Clear();

            for (int rowIndex = 0; rowIndex < txtLines.Count; rowIndex++)
            {
                if (ignoreRow.Contains(rowIndex))
                {
                    continue;
                }

                int sID = -1;
                T ormObj = Activator.CreateInstance<T>();

                string[] txtLine = txtLines[rowIndex].Split(new[] { "," }, StringSplitOptions.None);

                for (int col = 0; col < txtLine.Length; col++)
                {
                    if (!columnInfodDic.ContainsKey(col) || columnInfodDic[col].mArrayIndex > 0)
                    {
                        continue;
                    }

                    FieldInfo fInfo = columnInfodDic[col].mFieldInfo;
                    Type fType = fInfo.FieldType;

                    //不是数组，直接赋值
                    if (!fType.IsArray)
                    {
                        object value = mTypeDic.ContainsKey(fType) ? mTypeDic[fType](txtLine[col]) : txtLine[col];
                        fInfo.SetValue(ormObj, value);
                        //处理主键
                        if (col == 0)
                        {
                            int temp = 0;
                            if (int.TryParse(txtLine[col], out temp))
                            {
                                sID = temp;
                            }
                            else
                            {
                                if (rowIndex >= 3)
                                {
                                    ormObj = default(T);
                                    break;
                                }

                                Debug.LogFormat("{0}表的主键配置有错，应该为int型 行{1} 内容{2}", aAssetName, col, txtLine[col]);
                            }
                        }
                    }
                    else
                    {
                        //是数组，需查找所有的相同字段名
                        IList<int> arrayIndexs = (
                            from itr in columnInfodDic
                            where itr.Value.mColumnName == columnInfodDic[col].mColumnName
                            select itr.Key
                        ).ToList();

                        fType = fType.GetElementType();
                        Array objAry = Array.CreateInstance(fType, arrayIndexs.Count);
                        for (int k = 0; k < arrayIndexs.Count; k++)
                        {
                            objAry.SetValue(
                                mTypeDic.ContainsKey(fType)
                                    ? mTypeDic[fType](txtLine[arrayIndexs[k]])
                                    : txtLine[arrayIndexs[k]], k);
                        }

                        fInfo.SetValue(ormObj, objAry);
                    }
                }

                if (ormObj == null)
                {
                    continue;
                }

                if (aConfigDic.ContainsKey(sID))
                {
                    aConfigDic[sID] = ormObj;
                    Debug.LogWarning(string.Format("资源文件 <{0}> 的Key <{1}> 重复，系统自动进行覆盖", aAssetName, sID));
                }
                else
                {
                    aConfigDic.Add(sID, ormObj);
                }
            }
        }


        /// <summary>
        /// Orm映射字段的类型转换函数注册。
        /// </summary>
        private static void RegisterConvert()
        {
            mTypeDic = new Dictionary<Type, Func<string, object>>();
            //bool
            Func<string, object> cFunc = delegate (string aReadValue)
            {
                ushort rst;
                if (ushort.TryParse(aReadValue, out rst))
                {
                    return rst != 0;
                }

                return false;
            };
            mTypeDic.Add(typeof(bool), cFunc);
            //byte
            cFunc = delegate (string aReadValue)
            {
                byte rst;
                return !byte.TryParse(aReadValue, out rst) ? (byte)0 : rst;
            };
            mTypeDic.Add(typeof(byte), cFunc);
            //char
            cFunc = delegate (string aReadValue)
            {
                char rst;
                return !char.TryParse(aReadValue, out rst) ? '0' : rst;
            };
            mTypeDic.Add(typeof(char), cFunc);
            //decimal
            cFunc = delegate (string aReadValue)
            {
                decimal rst;
                return !decimal.TryParse(aReadValue, out rst) ? 0 : rst;
            };
            mTypeDic.Add(typeof(decimal), cFunc);
            //double
            cFunc = delegate (string aReadValue)
            {
                double rst;
                return !double.TryParse(aReadValue, out rst) ? 0 : rst;
            };
            mTypeDic.Add(typeof(double), cFunc);
            //float
            cFunc = delegate (string aReadValue)
            {
                float rst;
                return !float.TryParse(aReadValue, out rst) ? 0f : rst;
            };
            mTypeDic.Add(typeof(float), cFunc);
            //int
            cFunc = delegate (string aReadValue)
            {
                int rst;
                return !int.TryParse(aReadValue, out rst) ? 0 : rst;
            };
            mTypeDic.Add(typeof(int), cFunc);
            //long
            cFunc = delegate (string aReadValue)
            {
                long rst;
                return !long.TryParse(aReadValue, out rst) ? 0 : rst;
            };
            mTypeDic.Add(typeof(long), cFunc);
            //sbyte
            cFunc = delegate (string aReadValue)
            {
                sbyte rst;
                return !sbyte.TryParse(aReadValue, out rst) ? (sbyte)0 : rst;
            };
            mTypeDic.Add(typeof(sbyte), cFunc);
            //short
            cFunc = delegate (string aReadValue)
            {
                short rst;
                return !short.TryParse(aReadValue, out rst) ? (short)0 : rst;
            };
            mTypeDic.Add(typeof(short), cFunc);
            //string 不需要类型转换
            //uint
            cFunc = delegate (string aReadValue)
            {
                uint rst;
                return !uint.TryParse(aReadValue, out rst) ? 0 : rst;
            };
            mTypeDic.Add(typeof(uint), cFunc);
            //ushort
            cFunc = delegate (string aReadValue)
            {
                ushort rst;
                return !ushort.TryParse(aReadValue, out rst) ? (ushort)0 : rst;
            };
            mTypeDic.Add(typeof(ushort), cFunc);
        }
    }
}